package websocket.piggy.handler;

import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.ObjectUtil;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.http.DefaultHttpHeaders;
import io.netty.handler.codec.http.HttpClientCodec;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.websocketx.*;
import io.netty.handler.codec.http.websocketx.extensions.compression.WebSocketClientCompressionHandler;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
import org.springframework.stereotype.Component;
import websocket.piggy.PCMToWAVConverter;
import websocket.piggy.PcmCovWavUtil;
import websocket.piggy.WebsocketMap;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URI;
import java.nio.ByteBuffer;


//@Component
//@ServerEndpoint(value = "/websocket/{aaa}/{userId}")
public class WebsocketNettyServer {
    private String userId;
    private Channel channel;

    public String getUserId() {
        return userId;
    }

    private static final String URL = System.getProperty("url", "wss://ai.bobfintech.com.cn/multimodal/dangjian/asr/asr_online");

    //前端请求时一个websocket时
    @OnOpen
    public void onOpen(Session session, @PathParam("userId") String userId) {
        this.userId = userId;

        EventLoopGroup group = new NioEventLoopGroup(1);
        try {
            URI uri = new URI(URL);
            String scheme = uri.getScheme() == null ? "ws" : uri.getScheme();
            final String host = uri.getHost() == null ? "127.0.0.1" : uri.getHost();
            final int port;
            if (uri.getPort() == -1) {
                if ("ws".equalsIgnoreCase(scheme)) {
                    port = 80;
                } else if ("wss".equalsIgnoreCase(scheme)) {
                    port = 443;
                } else {
                    port = -1;
                }
            } else {
                port = uri.getPort();
            }

            if (!"ws".equalsIgnoreCase(scheme) && !"wss".equalsIgnoreCase(scheme)) {
                System.err.println("Only WS(S) is supported.");
                return;
            }

            boolean ssl = "wss".equalsIgnoreCase(scheme);
            SslContext sslCtx;
            if (ssl) {
                sslCtx = SslContextBuilder.forClient().trustManager(InsecureTrustManagerFactory.INSTANCE).build();
            } else {
                sslCtx = null;
            }
            WebSocketClientHandshaker shaker = WebSocketClientHandshakerFactory.newHandshaker(uri,
                    WebSocketVersion.V13, null, true, new DefaultHttpHeaders(), 100 * 1024 * 1024);
            WebsocketNettyClientHandler handler = new WebsocketNettyClientHandler(shaker);
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(group)
                    .channel(NioSocketChannel.class)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) {
                            ChannelPipeline p = ch.pipeline();
                            if (sslCtx != null) {
                                p.addLast(sslCtx.newHandler(ch.alloc(), host, port));
                            }
                            p.addLast(new HttpClientCodec());
                            p.addLast(new HttpObjectAggregator(65536));
                            p.addLast(WebSocketClientCompressionHandler.INSTANCE);
                            p.addLast(handler);
                        }
                    });
            this.channel = bootstrap.connect(host, port).sync().channel();
            handler.handshakeFuture().sync();
        }catch (Exception e){
            this.channel.close();
            group.shutdownGracefully();
        }
    }

    //前端关闭时一个websocket时
    @OnClose
    public void onClose(Session session, CloseReason closeReason) throws IOException {
        session.close();
        this.channel.close();
        WebsocketMap.concurrentHashMap.remove(this.userId);
        System.out.println("close");
    }

    //前端向后端发送消息
    @OnMessage
    public void onMessage(Session session, String message) {
        TextWebSocketFrame textWebSocketFrame = new TextWebSocketFrame(message);
        channel.writeAndFlush(textWebSocketFrame);
        System.out.println("onMessage string -> " + message);
    }

    @OnError
    public void onError(Session session, Throwable error) {
        error.printStackTrace();
    }

    @OnMessage
    public void onMessage(Session session, ByteBuffer message) throws IOException {
        WebSocketFrame binaryWebSocketFrame = new BinaryWebSocketFrame(Unpooled.wrappedBuffer(message));;
        ChannelFuture channelFuture = channel.writeAndFlush(binaryWebSocketFrame);
        channelFuture.addListener((ChannelFutureListener) future -> {
            boolean success = future.isSuccess();
            if(!success){
                System.out.println("send byte array error");
            }
        });

//        session.close();

        if (ObjectUtil.isEmpty(message)) {
            return;
        }

        // 开始
        PcmCovWavUtil.addBytesToFile("D:\\test\\" + userId, message.array());

        // 整体录音存储
        appendBuffer(message.array());
        try {
            // 整体音频存储
            ByteArrayOutputStream stream = WebsocketMap.concurrentHashMap.get(this.getUserId());
            if (stream != null) {
                ByteArrayOutputStream tempStream = new ByteArrayOutputStream();
                PcmCovWavUtil.convertWaveFile(stream, tempStream);
                byte[] byteArray = tempStream.toByteArray();
//                byte[] byteArray = PcmCovWavUtil.createWavFromPcm(stream.toByteArray(), 16000, 1, 16);

                // todo 1
                ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArray);
                FileOutputStream fileOutputStream = new FileOutputStream("D:\\test\\" + this.getUserId() + ".wav");
                IoUtil.copy(byteArrayInputStream, fileOutputStream);
                fileOutputStream.flush();
                fileOutputStream.close();
                // todo 2
                PCMToWAVConverter.convertPCMToWAV(stream.toByteArray(), this.getUserId() + "-v2.wav");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void appendBuffer(byte[] bytes) {
        ByteArrayOutputStream byteArrayOutputStream = WebsocketMap.concurrentHashMap.get(this.getUserId());
        try {
            if (byteArrayOutputStream == null) {
                byteArrayOutputStream = new ByteArrayOutputStream();
            }
            byteArrayOutputStream.write(bytes);
        } catch (IOException e) {
            e.printStackTrace();
            try {
                byteArrayOutputStream.close();
            } catch (IOException ex) {
                ex.printStackTrace();
            }
            return;
        }
        WebsocketMap.concurrentHashMap.put(this.getUserId(), byteArrayOutputStream);
    }
}
